Skip to content

fix(wasm): close DNS-rebinding TOCTOU in the plugin HTTP client#100

Open
ndreno wants to merge 1 commit into
mainfrom
sec/dns-rebinding-resolver
Open

fix(wasm): close DNS-rebinding TOCTOU in the plugin HTTP client#100
ndreno wants to merge 1 commit into
mainfrom
sec/dns-rebinding-resolver

Conversation

@ndreno

@ndreno ndreno commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

What

The SSRF guard resolves the target host and checks the IPs, but reqwest re-resolves DNS when it connects. A hostile resolver can return a public IP for the pre-flight check, then rebind to an internal address for the actual connection (TOCTOU) — bypassing the guard.

Fix

Install a custom reqwest DNS resolver (GuardedResolver) on both the default and TLS clients. It resolves and filters internal / loopback / link-local / cloud-metadata addresses in one step. reqwest connects to exactly the addresses it returns and does not resolve again, so the address that is checked is the address that is connected to. When egress is disallowed and every resolved address is internal, resolution fails (blocked).

The per-request pre-flight ssrf_guard is kept for early, clear errors and literal-IP targets; the resolver is the TOCTOU-safe enforcement point.

Scope

Closes the AR (DNS-rebinding residual) item in private #9. With it, #9's other items (DP-4/5/8) having landed in #97, #9 should be fully addressed.

Tests

  • Unit test for the address filter (internal + link-local dropped when egress disallowed; empty result = blocked; all pass through when allowed).
  • Verified locally: security SSRF suite (loopback / RFC1918 / metadata / DNS-resolved-metadata all blocked, 4/4), and egress-allowed paths reach loopback wiremock (ai_proxy 19, proxy 18).

The SSRF guard resolves the target host and checks the IPs, but reqwest
re-resolves DNS when it actually connects. A hostile resolver can answer
with a public IP for the pre-flight check and then rebind to an internal
address for the connection (TOCTOU), bypassing the guard.

Install a custom reqwest DNS resolver (GuardedResolver) that resolves and
filters internal/loopback/link-local/metadata addresses in one step, on
both the default and TLS clients. reqwest connects to exactly the vetted
addresses it returns and does not resolve again, so the address that is
checked is the address that is connected to. When egress is disallowed and
every resolved address is internal, resolution fails (blocked). The
per-request pre-flight ssrf_guard is kept for early, clear errors and
literal-IP targets.

Addresses AR (DNS-rebinding residual) in private #9.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant